package com.fuint.utils.syb;

import cn.hutool.core.codec.Base64;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonAlias;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;

public class SybUtil {
    /**
     * js转化为实体
     *
     * @param <T>
     * @param jsonstr
     * @param cls
     * @return
     */
    public static <T> T json2Obj(String jsonStr, Class<T> cls) {
        return (T) JSONUtil.toBean(jsonStr, cls);
    }

    /**
     * md5
     *
     * @param b
     * @return
     */
    public static String md5(byte[] b) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.reset();
            md.update(b);
            byte[] hash = md.digest();
            StringBuilder outStrBuf = new StringBuilder(32);
            for (int i = 0; i < hash.length; i++) {
                int v = hash[i] & 0xFF;
                if (v < 16) {
                    outStrBuf.append('0');
                }
                outStrBuf.append(Integer.toString(v, 16).toLowerCase());
            }
            return outStrBuf.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return new String(b);
        }
    }

    /**
     * 判断字符串是否为空
     *
     * @param s
     * @return
     */
    public static boolean isEmpty(String s) {
        if (s == null || s.trim().isEmpty())
            return true;
        return false;
    }

    /**
     * 生成随机码
     *
     * @param n
     * @return
     */
    public static String getValidatecode(int n) {
        Random random = new Random();
        String sRand = "";
        // default 4
        n = n == 0 ? 4 : n;
        for (int i = 0; i < n; i++) {
            String rand = String.valueOf(random.nextInt(10));
            sRand += rand;
        }
        return sRand;
    }


    public static boolean validSign(TreeMap<String, String> param, String appKey, String signType) throws Exception {
        if (param != null && !param.isEmpty()) {
            if (!param.containsKey("sign"))
                return false;
            String sign = param.remove("sign");
            // 如果是md5则需要把md5的key加入到排序
            if ("MD5".equals(signType)) {
                param.put("key", appKey);
            }
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> entry : param.entrySet()) {
                if (entry.getValue() != null && !entry.getValue().isEmpty()) {
                    sb.append(entry.getKey()).append("=")
                            .append(entry.getValue()).append("&");
                }
            }
            if (sb.length() > 0) {
                sb.deleteCharAt(sb.length() - 1);
            }
            if ("MD5".equals(signType)) {
                return sign.toLowerCase().equals(
                        md5(sb.toString().getBytes(StandardCharsets.UTF_8)).toLowerCase());
            } else if ("SM2".equals(signType)) {
                PublicKey publicKey = SmUtil.pubKeySM2FromBase64Str(appKey);
                return SmUtil.verifySM3SM2(publicKey, "Allinpay", Base64.decode(sign), sb.toString().getBytes(StandardCharsets.UTF_8));
            } else {
                return rsaVerifyPublickey(sb.toString(), sign, appKey, "UTF-8");
            }
        }
        return false;
    }

    public static boolean rsaVerifyPublickey(String content, String sign, String publicKey, String charset) throws Exception {
        try {
            PublicKey pubKey = getPublicKeyFromX509("RSA", Base64.decode(publicKey.getBytes()));
            return rsaVerifyPublickey(content, sign, pubKey, charset);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
        }
    }

    public static boolean rsaVerifyPublickey(String content, String sign, PublicKey pubKey, String charset) throws Exception {
        java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
        signature.initVerify(pubKey);
        if (charset == null || charset.isEmpty()) {
            signature.update(content.getBytes());
        } else {
            signature.update(content.getBytes(charset));
        }

        return signature.verify(Base64.decode(sign.getBytes()));
    }

    /**
     * 实体类转TreeMap
     *
     * @param entity 实体类
     * @return TreeMap<String, Object>
     */
    public static TreeMap<String, String> parseEntity(Object entity) {
        TreeMap<String, String> map = new TreeMap<>();
        Field[] fields = entity.getClass().getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                String fieldName = field.getName();
                String fieldValue = (String) field.get(entity);
                // 带注解，则取注解中的值
                JsonAlias jsonAlias = field.getAnnotation(JsonAlias.class);
                if (jsonAlias != null) {
                    String[] aliases = jsonAlias.value();
                    map.put(aliases[0], fieldValue);
                } else {
                    map.put(fieldName, fieldValue);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    /**
     * 1. 移除 sign key
     * 2. 如果signType = MD5 则添加 key = appKey
     * 3. 遍历KV，组成k=v&k=v格式的字符串，
     * 4. 使用 MD5 / SM2 / RSA 方式签名
     *
     * @param params
     * @param appKey
     * @param signType
     * @return
     * @throws Exception
     */
    public static String unionSign(TreeMap<String, String> params, String appKey, String signType) throws Exception {
        params.remove("sign");
        // 如果是md5则需要把md5的key加入到排序
        if ("MD5".equals(signType)) {
            params.put("key", appKey);
        }
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (entry.getValue() != null && !entry.getValue().isEmpty()) {
                sb.append(entry.getKey()).append("=").append(entry.getValue())
                        .append("&");
            }
        }
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        String sign = "";
        if ("MD5".equals(signType)) {
            System.out.println(sb);
            // 记得是md5编码的加签
            sign = md5(sb.toString().getBytes(StandardCharsets.UTF_8));
            params.remove("key");
        } else if ("SM2".equals(signType)) {
            System.out.println(sb);
            PrivateKey privateKey = SmUtil.privKeySM2FromBase64Str(appKey);
            //签名
            sign = SmUtil.signSM3SM2RetBase64(privateKey, params.get("appid"), sb.toString().getBytes(StandardCharsets.UTF_8));
        } else {
            System.out.println(sb);
            sign = rsaSign(sb.toString(), appKey, "UTF-8");
        }
        return sign;
    }

    public static String rsaSign(String content, String privateKey, String charset) throws Exception {
        PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", Base64.decode(privateKey.getBytes()));
        return rsaSign(content, priKey, charset);
    }

    public static String rsaSign(String content, byte[] privateKey, String charset) throws Exception {
        PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", privateKey);
        return rsaSign(content, priKey, charset);
    }

    public static String rsaSign(String content, PrivateKey priKey, String charset) throws Exception {
        java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
        signature.initSign(priKey);
        if (charset == null || charset.isEmpty()) {
            signature.update(content.getBytes());
        } else {
            signature.update(content.getBytes(charset));
        }
        byte[] signed = signature.sign();

        return Base64.encode(signed);
    }

    public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, byte[] encodedKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    }

    public static PublicKey getPublicKeyFromX509(String algorithm, byte[] encodedKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    }
}
